/* Examples/jerasure_05.c
 * James S. Plank

Jerasure - A C/C++ Library for a Variety of Reed-Solomon and RAID-6 Erasure Coding Techniques
Copright (C) 2007 James S. Plank

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
  
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  
James S. Plank
Department of Electrical Engineering and Computer Science
University of Tennessee 
Knoxville, TN 37996
plank@cs.utk.edu
*/

/*
 * $Revision: 1.2 $
 * $Date: 2008/08/19 17:41:40 $
 */
    

/*
	revised by S. Simmerman
	2/25/08  
*/
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include "jerasure-2.h"

#define talloc(type, num) (type *) malloc(sizeof(type)*(num))

void usage(char *s)
{
	fprintf(stderr, "usage: jerasure_05 k m w size - Does a simple Reed-Solomon coding example in GF(2^w).\n");
	fprintf(stderr, "       \n");
	fprintf(stderr, "       k+m must be <= 2^w.  w can be 8, 16 or 32.\n");
	fprintf(stderr, "       It sets up a Cauchy distribution matrix and encodes\n");
	fprintf(stderr, "       k devices of size bytes with it.  Then it decodes.\n", sizeof(long));
	fprintf(stderr, "       After that, it decodes device 0 by using jerasure_make_decoding_matrix()\n");
	fprintf(stderr, "       and jerasure_matrix_dotprod().\n");
	fprintf(stderr, "       \n");
	fprintf(stderr, "This demonstrates: jerasure_matrix_encode()\n");
	fprintf(stderr, "                   jerasure_matrix_decode()\n");
	fprintf(stderr, "                   jerasure_print_matrix()\n");
	fprintf(stderr, "                   jerasure_make_decoding_matrix()\n");
	fprintf(stderr, "                   jerasure_matrix_dotprod()\n");
	if (s != NULL) fprintf(stderr, "\n%s\n\n", s);
	exit(1);
}

void print_data_and_coding(JER_Slices *slices)
{
	int i, j, x;
	int n, sp;
	int k, m, w, size;
	uint64_t l;

	k = slices->K;
	m = slices->M;
	w = slices->W;
	size = slices->size;

	if (k > m) n = k;
	else n = m;
	sp = size * 2 + size/(w/8) + 8;

	printf("%-*sCoding\n", sp, "Data");
	for (i = 0; i < n; i++) {
		if (i < k) {
			printf("D%-2d:", i);
			for (j = 0; j < size; j+=(w/8)) {
				printf(" ");
				for (x = 0; x < w/8; x++){
					printf("%02x", (unsigned char)slices->ptrs[i][j+x]);
				}
			}
			printf("    ");
		}
		else printf("%*s", sp, "");
		if (i < m) {
			printf("C%-2d:", i);
			for (j = 0 ; j < size; j+=(w/8)) {
				printf(" ");
				for (x = 0; x < w/8; x++){
					printf("%02x", (unsigned char)slices->ptrs[i+k][j+x]);
				}
			}
		}
		printf("\n");
	}
	printf("\n");
}

int main(int argc, char **argv)
{
	uint64_t l;
	int k, m, w, size;
	int i, j;
	int sou;
	JER_Matrix *jm;
	JER_Slices *slices;
	vector <int> erasures;
	vector <int> erased;
	JER_Matrix *dm;
	int *dm_ids;
	unsigned char *data;

	if (argc != 5) usage(NULL);
	if (sscanf(argv[1], "%d", &k) == 0 || k <= 0) usage("Bad k");
	if (sscanf(argv[2], "%d", &m) == 0 || m <= 0) usage("Bad m");
	if (sscanf(argv[3], "%d", &w) == 0 || (w != 8 && w != 16 && w != 32))
		usage("Bad w");
	if (w < 32 && k + m > (1 << w)) usage("k + m must be <= 2 ^ w");
	if (sscanf(argv[4], "%d", &size) == 0 || size % sizeof(uint64_t) != 0) 
		usage("size must be multiple of sizeof(long)");

	jm = new JER_Matrix(m, k, w);

	for (i = 0; i < m; i++) {
		for (j = 0; j < k; j++) {
			jm->Elts[i][j] = jm->gc->galois_single_divide(1, i ^ (m + j));
		}
	}

	printf("The Coding Matrix (the last m rows of the Distribution Matrix):\n\n");
	jm->Print();
	printf("\n");

	srand48(0);
	slices = new JER_Slices();
	slices->K = k;
	slices->M = m;
	slices->W = w;
	slices->PS = sizeof(uint64_t);
	slices->size = size;

	sou = sizeof(uint64_t);

	slices->ptrs.resize(k + m);

	data = (unsigned char *) malloc (size * (k + m));

	for (i = 0; i < k; i++) {
		for(j = 0; j < size; j+=sou) {
			l = lrand48();
			l <<= 8*4;
			l += lrand48();

			memcpy(data + i*size + j, &l, sou);
		}
		slices->ptrs[i] = data + i*size;
	}

	for (i = 0; i < m; i++) {
		slices->ptrs[k + i] = data + k*size + i*size;
	}

	JER_Matrix_Encode(slices, jm);

	printf("Encoding Complete:\n\n");
	print_data_and_coding(slices);

	erasures.resize(m);
	erased.resize(m+k);

	for (i = 0; i < m+k; i++) erased[i] = 0;
	l = 0;
	for (i = 0; i < m; ) {
		erasures[i] = lrand48()%(k+m);
		if (erased[erasures[i]] == 0) {
			erased[erasures[i]] = 1;
			bzero(slices->ptrs[erasures[i]], size);
			i++;
		}
	}

	printf("Erased %d random devices:\n\n", m);
	print_data_and_coding(slices);

	if (JER_Matrix_Decode(slices, jm, 0, erasures) < 0) exit(1);

	printf("State of the system after decoding:\n\n");
	print_data_and_coding(slices);

	dm_ids = talloc(int, k);
	dm = new JER_Matrix(k, k, w);

	for (i = 0; i < m; i++) erased[i] = 1;
	for (; i < k+m; i++) erased[i] = 0;

	JER_Make_Decoding_Matrix(jm, erased, dm, dm_ids);

	printf("Suppose we erase the first %d devices.  Here is the decoding matrix:\n\n", m);
	dm->Print();
	printf("\n");
	printf("And dm_ids:\n\n");
	for (i = 0; i < k; i++) {
		printf(" ");
		if (i % w == 0 && i > 0) printf(" ");
		printf("%d", dm_ids[i]);
	}
	printf("\n\n");

	bzero((char *)data, size);
	JER_Matrix_Dotprod(slices, dm, 0, dm_ids, 0);

	printf("\nAfter calling jerasure_matrix_dotprod, we calculate the value of device #0 to be:\n\n");
	printf("D0 :");
	for (i = 0; i < size; i+=(w/8)) {
		printf(" ");
		for (j = 0; j < w/8; j++){
			printf("%02x", (unsigned char)slices->ptrs[0][i+j]);
		}
	}
	printf("\n\n");

	return 0;
}
